<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
		<title>图片裁切</title>
		<script type="text/javascript">
			(function() {
				function adapterScreen() {
					let html = document.documentElement;
					// 获取html的宽度
					let htmlWidth = parseInt(html.getBoundingClientRect().width) // 可能有小数点，转为整数
					console.log(htmlWidth / 7.5)
					html.style.fontSize = htmlWidth / 750 * 100 + 'px'
					// rem = 实际宽/100 
				}
				adapterScreen()
				window.addEventListener('resize', adapterScreen)
			})()
		</script>
		<style type="text/css">
			html,
			body,
			* {
				margin: 0;
				padding: 0;
			}
			a{
				outline: none;
				text-decoration: none;
			}
			a:hover{}
			html{
				height: 100%;
			}
			body{
				width: 7.5rem;
				margin: 0 auto;
				height: 100%;
				overflow: hidden;
			}
			.m-header {
				width: 100%;
				display: flex;
				align-items: center;
				justify-content: center;
				font-size: 0.32rem;
				height: 0.88rem;
				text-align: center;
				box-shadow: 0 0.05rem 0.3rem rgba(200, 200, 200, 0.5);
				margin-bottom: 0.02rem;
			}
			.upload-area{
				position: absolute;
				left: 50%;
				top: 50%;
				transform: translate(-50%,-50%);
				display: flex;
				flex-direction: column;
				justify-content: center;
				align-items: center;
				font-size: 0.32rem;
				width: 3rem;
				height: 3rem;
				border: 0.01rem solid #EEEEEE;
				background: #f7f7f7;
				color: #999;
			}
			.upload-area-img{
				display: none;
				position: absolute;
				left: 0;
				top: 0;
				width: 3rem;
				height: 3rem;
			}
			.cropper-wrap {
				position: relative;
				width: 7.5rem;
				height: 7.5rem;
				box-sizing: border-box;
				background-color: #fff;
			}

			.cropper-canvas {
				width: 7.5rem;
				height: 7.5rem;
				box-sizing: border-box;
				
			}
			.cropper-mask {
				position: absolute;
				left: 0;
				top: 0;
				width: 3rem;
				height: 3rem;
				box-sizing: border-box;
				border: 0.01rem dashed rgba(255, 255, 255, 0.8);
				pointer-events: none;
				background-color: rgba(0,0,0,0.2);
			}

			.cropper-mask-line-x,
			.cropper-mask-line-y {
				position: absolute;
				left: 0;
				top: 0;
				width: 100%;
				height: 100%;
			}

			.cropper-mask-line-x:before,
			.cropper-mask-line-x:after {
				content: '';
				position: absolute;
				height: 1px;
				width: 100%;
				background-color: rgba(255, 255, 255, 0.5);
			}

			.cropper-mask-line-x:before {
				top: 33%;
			}

			.cropper-mask-line-x:after {
				top: 66%;
			}

			.cropper-mask-line-y:before,
			.cropper-mask-line-y:after {
				content: '';
				position: absolute;
				width: 1px;
				height: 100%;
				background-color: rgba(255, 255, 255, 0.5);
			}

			.cropper-mask-line-y:before {
				left: 33%;
			}

			.cropper-mask-line-y:after {
				left: 66%;
			}
			.scale-area{
				position: absolute;
				right: 0.2rem;
				top: 50%;
				transform: translateY(-50%);
			}
			.scale-btn{
				width: 0.5rem;
				height: 0.5rem;
				background: #FFFFFF;
				border-radius:1rem;
				text-align: center;
				line-height: 0.5rem;
				font-size: 0.32rem;
				margin-bottom: 0.2rem;
				cursor: pointer;
				box-shadow: 0 0.1rem 0.1rem rgba(0,0,0,0.2);
			}
			.m-footer{
				position: absolute;
				left: 0;
				right: 0;
				bottom: 0;
				display: flex;
				height: 0.88rem;
			}
			.m-footer:before{
				content: '';
				position: absolute;
				left: 0;
				right: 0;
				height: 0.01rem;
				background: #eee;
				font-size: 0;
			}
			.m-footer a{
				float: left;
				display: inline-block;
				position: relative;
				height: 100%;
				width: 50%;
				line-height: 0.88rem;
				color: #545454;
				font-size:0.32rem;
				text-align: center;
			}
			.m-footer a:after{
				content: '';
				position: absolute;
				right: 0;
				height: 100%;
				width: 0.01rem;
				background: #eee;
			}
		</style>
	</head>
	<body>
		<header class="m-header">图片裁切</header>
		<div class="upload-area">
				<p>+</p>
				<p>点击上传</p>
				<img class="upload-area-img" src="" >
		</div>
		<div class="cropper-wrap" style="display: none;">
			<canvas id="cropper-canvas" class="cropper-canvas"></canvas>
			<div class="cropper-mask">
				<div class="cropper-mask-line-x"></div>
				<div class="cropper-mask-line-y"></div>
			</div>
			<div class="scale-area">
				<div class="scale-btn zoom-in">+</div>
				<div class="scale-btn zoom-out">-</div>
			</div>
		</div>
		<div class="m-footer"  style="display: none;">
			<a href="javascript:;" class="upload">重新上传</a>
			<a href="javascript:;" class="submit">确定</a>
		</div>
		<input class="upload-input" style="opacity: 0;position: absolute;right: -999px;" type="file" name="file" accept="image/*" />
		<script type="text/javascript">
			/**
			 * 1.设置画布大小，占满全屏剩余空间,设置遮罩区域选择位置
			 * 2.选择图片，通过FileReader读取文件，转为base64,将图片绘制到画布中
			 * 3.通过按钮放大缩小图片,touch拖拽图片
			 * 4.点击确定生成图片
			 */
			class PageCropper{
				constructor() {
				    this.wrap = document.querySelector('.cropper-wrap')
				    this.area = document.querySelector(".cropper-mask");
					this.header = document.querySelector(".m-header");
					this.footer = document.querySelector(".m-footer");
					this.uploadArea = document.querySelector(".upload-area");
					
					this.uploadAreaImg =document.querySelector(".upload-area-img");
					this.uploadBtn = document.querySelector('.upload') 
					this.uploadInputBtn = document.querySelector('.upload-input') 
					this.submitBtn = document.querySelector('.submit') 
					this.zoomInBtn = document.querySelector('.zoom-in')
					this.zoomOutBtn = document.querySelector('.zoom-out')
					// 放大缩小值
					this.zoomInoutNum = 50
					// canvas
				    this.c = document.querySelector('#cropper-canvas');
					this.ctx = this.c.getContext('2d');
					// 上传的图片
					this.pic = null;
					
					// canvas 的 属性
					this.cRect = {
						w:0,
						h:0
					}
					// 
					this.maskRect = {
						w:0,
						h:0
					}
					this.maskRect.w = this.uploadArea.offsetWidth
					this.maskRect.h = this.uploadArea.offsetHeight
					// 上传图片 的 属性
					this.picRect={
						x:0,
						y:0,
						w:0,
						h:0
					}
					// 拖拽 
					this.offset = {
						x:0,
						y:0
					}
					// 设置元素样式
					this.setElStyle()
					// 绑定事件
					this.bindElEvent()
					window.addEventListener('resize',()=>{
						this.setElStyle()
					})
				}
				bindElEvent(){
					// 上传按钮点击
					this.uploadArea.addEventListener('click',()=>{
						this.uploadInputBtn.click()
					})
					this.uploadBtn.addEventListener('click',()=>{
						this.uploadInputBtn.click()
					})
					// file input change
					this.uploadInputBtn.addEventListener('change',(e)=>{this.fileChange(e)})
					// 点击放大
					this.zoomInBtn.addEventListener('click',(e)=>{this.zoomIn(e)})
					// 点击缩小
					this.zoomOutBtn.addEventListener('click',(e)=>{this.zoomOut(e)})
					this.c.addEventListener('touchstart',(e)=>{this.touchStart(e)})
					
					// 确定生成图片
					this.submitBtn.addEventListener('click',(e)=>{this.submit(e)})
				}
				// 1.设置画布大小，占满全屏剩余空间,设置遮罩区域选择位置
				setElStyle(){
					 //设置画布大小，占满全屏剩余空间	
					 let windowRect = document.documentElement.getBoundingClientRect()
					 let headerH = document.querySelector('.m-header').offsetHeight;
					 let footerH = document.querySelector('.m-footer').offsetHeight;
					 let containerH= windowRect.height - headerH - footerH
					 this.cRect.w = windowRect.width
					 this.cRect.h = containerH
					 // 设置canvas宽高
					 this.c.style.height = this.wrap.style.height = this.cRect.h + 'px'
					 this.c.style.width = this.wrap.style.width = this.cRect.w + 'px'
					 this.c.height = this.cRect.h
					 this.c.width = this.cRect.w
					 // 设置遮罩区域选择位置
					 this.maskRect.x = (windowRect.width-this.area.offsetWidth) / 2
					 this.maskRect.y = (containerH-this.area.offsetHeight) / 2 
					
					 this.area.style.left =  this.maskRect.x +'px'
					 this.area.style.top = this.maskRect.y+'px'
				}
				showCropperPage(){
					this.uploadArea.style.display="none"
					this.wrap.style.display="block"
					this.footer.style.display="block"
					this.setElStyle()
				}
				hideCropperPage(){
					this.uploadArea.style.display="block"
					this.wrap.style.display="none"
					this.footer.style.display="none"
					this.uploadAreaImg.style.display="block"
				}
				// 2.选择图片，通过FileReader读取文件，转为base64,将图片绘制到画布中
				fileChange(e){
					
					let file = e.target.files[0]
					if(!file) return false;
					// 选择文件后，显示裁切界面
					this.showCropperPage()
					// 通过FileReader读取文件
					let pic = new FileReader()
					pic.readAsDataURL(file)
					pic.onload=(e=>{
						this.pic = new Image()
						this.pic.src = e.target.result
						// 图片加载成功，计算宽度，绘制图片
						this.pic.onload = (e)=>{
							this.picRect.w = this.pic.width
							this.picRect.h = this.pic.height
							// 计算图片的宽高位置
							this.calcRatio()
							// 绘制图片
							this.drawImg()
						}
					})
				}
				// 计算图片显示的宽高位置
				calcRatio(){
					let n = 1;//比例系数
					if(this.picRect.w > this.picRect.h){
						n = this.picRect.w / this.cRect.w
						this.picRect.w = this.cRect.w
						this.picRect.h = this.picRect.h / n
					}
					if(this.picRect.w < this.picRect.h){
						n = this.picRect.h / this.cRect.h
						this.picRect.h = this.cRect.h
						this.picRect.w = this.picRect.w / n
					}
					this.picRect.x = (this.cRect.w- this.picRect.w)/2
					this.picRect.y = (this.cRect.h- this.picRect.h)/2
				}
				// 缩小
				zoomOut(){
					if(!this.pic) return 
					this.calcScaleVal(-1)
					this.drawImg()
				}
				// 放大
				zoomIn(){
					if(!this.pic) return 
					this.calcScaleVal(1)
					this.drawImg()
				}
				calcScaleVal(val){
					let h = (this.picRect.w +this.zoomInoutNum*val)*this.picRect.h / this.picRect.w;
					this.picRect.w += this.zoomInoutNum*val
					this.picRect.h = h
				}
				touchStart(e){
					e.preventDefault()
					let touch = e.changedTouches[0]
					this.offset = {
						x:touch.clientX,
						y:touch.clientY
					}
					
					document.addEventListener('touchmove',(e)=>{this.touchMove(e)})
					document.addEventListener('touchend',()=>{this.touchEnd()})
				}
				touchMove(e){
					let touch = e.changedTouches[0]
					let disx = touch.clientX - this.offset.x
					let disy = touch.clientY - this.offset.y
					this.picRect.x += disx
					this.picRect.y += disy
					this.drawImg()
					this.offset = {
						x:touch.clientX,
						y:touch.clientY
					}
				}
				touchEnd(){
					document.removeEventListener('touchmove',(e)=>{this.touchMove(e)})
					document.removeEventListener('touchend',()=>{this.touchEnd()})
				}
				// 绘制图片
				drawImg(){
					this.ctx.clearRect(0,0,this.cRect.w,this.cRect.h)
					this.ctx.drawImage(this.pic,this.picRect.x,this.picRect.y,this.picRect.w,this.picRect.h)
				}
				submit(){
					if(!this.pic) return 
					let c2 = document.createElement('canvas')
					let ctx2 = c2.getContext('2d')
					c2.width = this.maskRect.w
					c2.height = this.maskRect.h
					
					// 获取需要裁切的图像数据
					let c1imgData = this.ctx.getImageData(this.maskRect.x,this.maskRect.y,this.maskRect.w,this.maskRect.h)
					
					// 将图像数据写入新的canvas
					ctx2.putImageData(c1imgData,0,0,0,0,this.maskRect.w,this.maskRect.h)
					
					// 将canvas转为图片
					this.uploadAreaImg.src = c2.toDataURL('image/png')
					this.hideCropperPage()
				}
			}
			let cropper = new PageCropper() 
			 
			 
		</script>
	</body>
</html>
